/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief SQLResult used datatypes, split to own file for clarity.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#ifndef _MACI_SQL_SQLCONTAINERTYPES_HPP_
#define _MACI_SQL_SQLCONTAINERTYPES_HPP_

#include <string>
#include <vector>
#include <map>
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>

namespace MaCI {
  namespace SQL {
    
    /** Enumeration Key to Value array. 
     * 
     * This array maps a Enumeration string to a numeric value.
     */
    typedef std::map<std::string, unsigned int> TEnumerationKeyToValueArray;


    /** Enumeration Value to Key array. 
     * 
     * This array maps a Enumeration value to enumeration key.
     */
    typedef std::map<unsigned int, std::string> TEnumerationValueToKeyArray;


    /** Single SQL Field.
     *
     */
    class CSQLField 
    {
    public:
      CSQLField(const std::string &aValueString = "")
        : iValueString(aValueString),
          iIsNULL(false)
      {}
      
      ~CSQLField() {}


      /** Set NULL status. If NULL is set, any value is cleared.
       *
       * @param[in] aIsNULL     If 'true' field is NULL. 
       *                        'false' otherwise.
       */
      void SetNULL(bool aIsNULL) {
        if (aIsNULL) iValueString = "NULL";
        iIsNULL = aIsNULL;
      }


      /** Set Value.
       *
       */
      void SetValueString(const std::string &aValueString) {
         iValueString = aValueString;
         if (iIsNULL) iIsNULL = false;
      }
      
      /** Return the field value string.
       *
       */
      const std::string &GetValueString(void) const {
        return iValueString;
      }


      /** Convert value string to unsigned int.
       * 
       * \note This function is exactly the same as GetValueString(), except
       * that the parameter is output through parameter reference.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                This function always returns 'true'.
       */
      bool ConvertTo(std::string &aValue) const {
        aValue = iValueString;
        return true;
      }


      /** Convert value string to unsigned int.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(unsigned int &aValue) const {
        if (sscanf(iValueString.c_str(),"%u", &aValue) != 1) return false; 
        return true;
      }
      
      /** Convert value string to int.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(int &aValue) const {
        if (sscanf(iValueString.c_str(),"%d", &aValue) != 1) return false; 
        return true;
      }

      /** Convert value string to unsigned long int.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(unsigned long int &aValue) const {
        if (sscanf(iValueString.c_str(),"%lu", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to int32_t.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(long int &aValue) const {
        if (sscanf(iValueString.c_str(),"%ld", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to unsigned long long int.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(unsigned long long int &aValue) const {
        if (sscanf(iValueString.c_str(),"%llu", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to long long int.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(long long int &aValue) const {
        if (sscanf(iValueString.c_str(),"%lld", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to float.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(float &aValue) const {
        if (sscanf(iValueString.c_str(),"%f", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to double.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(double &aValue) const {
        if (sscanf(iValueString.c_str(),"%lf", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to long double.
       * 
       * @param[out] aValue     Reference to store the converted value.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(long double &aValue) const {
        if (sscanf(iValueString.c_str(),"%Lf", &aValue) != 1) return false; 
        return true;
      }


      /** Convert value string to enumeration value.
       * 
       * This function attempts to convert the currently stored string
       * to enumeration value by searching for the string from the
       * given EnumerationKey array. If a match is found, the integer
       * value is stored to given output reference and true is
       * returned.  If no match is found, the function returns an
       * error.
       *
       * @param[out] aEnumeration Reference to store the converted value.
       * @param[in] aEnumerationKey Array of enumeration keys to match against.
       * @return                'true' if conversion was succesfull,
       *                        'false' on conversion failure.
       */
      bool ConvertTo(int &aEnumeration, const TEnumerationKeyToValueArray &aEnumerationKey) const {
        TEnumerationKeyToValueArray::const_iterator i = aEnumerationKey.find(iValueString);
        if (i != aEnumerationKey.end()) {
          aEnumeration = i->second;
          return true;
        }
        return false;
      }

      /** Is Field value NULL?
       * 
       * @return                'true' if field is NULL, otherwise 'false'.
       */
      bool IsNULL(void) const {
        return iIsNULL;
      }

    private:
      std::string iValueString;
      bool iIsNULL; ///< Is field value NULL?
    };
    

    // Array of CSQLFields == SQL Row
    typedef std::vector<CSQLField> TSQLRow;

    // Array of TSQLRows, meaning multiple rows.
    typedef std::vector<TSQLRow> TSQLRowArray;

    // One more type.
    class CSQLTable {
    public:
      CSQLTable()
        : iRowsCount(0),
          iColumnsCount(0),
          iRows() {}

      CSQLTable(const std::string &aTableName) 
        : iRowsCount(0),
          iColumnsCount(0),
          iRows() {}

      ~CSQLTable() {}


      /** Clear container.
       *
       */
      void Reset(void) {
        iRows.clear();
        iRowsCount = 0;
        iColumnsCount = 0;
      }

      /** Set table dimensions.
       *
       */
      bool SetDimensions(const unsigned int aRows, const unsigned int aColumns) 
      {
        bool result = true;
        
        // Store rows + columns
        iRowsCount = aRows;
        iColumnsCount = aColumns;
        
        // Resize rows.
        iRows.reserve(iRowsCount);

        // Then each column in fields is resized to size of rows.
        // Also note that is function already assigns the index number.
        for(TSQLRowArray::iterator i = iRows.begin();
            i != iRows.end();
            ++i) {
          i->reserve(iColumnsCount);
        }

        return result;
      }

      /** Set field contents by row/column.
       *
       * This function assigns a field value by row/column key.
       * @param[in] aRow        Row of SQL table (0 is first)
       * @param[in] aCol        Column of SQL table (0 is first)
       * @param[in] aField      Field data to set.
       * @return                'true' if succesfully added. 'false'
       *                        if index (col or row) out of bounds.
       */
      bool SetField(const unsigned int aRow, const unsigned int aCol,
                    const CSQLField &aField) {
        if (aRow < iRowsCount && aCol < iColumnsCount) {
          iRows.at(aRow).at(aCol) = aField;
          return true;

        } else {
          return false;

        }
      }


      /** Get reference to field by row/column.
       * 
       * @param[in] aRow        Target row.
       * @param[in] aCol        Target col.
       * @return                Reference to specified field item.
       */
      const CSQLField &GetField(const unsigned int aRow, const unsigned int aCol) const {
        assert(aRow < iRowsCount && aCol < iColumnsCount);
        return iRows.at(aRow).at(aCol);
      }


      /** Add new Row. New row is just pushed back in the container.
       *
       */
      bool AddRow(const TSQLRow &aNewRow) {
        bool result = false;
        if (aNewRow.size() == iColumnsCount && 
            iRows.size() <= iRowsCount) {
          // Checks ok.
          iRows.push_back(aNewRow);
          result = true;
        }
        return result;
      }


      /** Return reference to single Row.
       *
       *
       */
      const TSQLRow &GetRow(const unsigned int aRowIndex) const {
        return iRows[aRowIndex];
      }


      /** Return number of rows in container.
       * 
       * @return                Number of rows in container.
       */
      unsigned int GetRowCount(void) const {
        return iRowsCount;
      }


      /** Return number of columns in container.
       *  
       * @return                Number of columns in container.
       */
      unsigned int GetColumnCount(void) const {
        return iColumnsCount;
      }
      

    private:
      unsigned int iRowsCount;                 ///< Number of rows in tables
      unsigned int iColumnsCount;              ///< Numbers of columns in tables
      TSQLRowArray iRows;                 ///< Array of Rows (data)
    };
  }
}
#endif //_MACI_SQL_SQLCONTAINERTYPES_HPP_
